/********************************************************************
 * main.c
 *
 * Program to scroll text across a 5 by 7 LED matrix.
 * Compiled with TI Code Composer Studio for MSP430G2 MCU
 * on the LaunchPad development board.
 *
 * Author: Ben Han (benhan82@gmail.com)
 * Date: 23 July 2012
 */

#include <msp430.h>
#include <cstring>


/**
 * Global variable definitions
 */

#define MAX_CHARS 		50
#define	SCROLL_SPEED	250		// smaller value will scroll faster
#define LED_DELAY		8

// User input string
const char input[MAX_CHARS] = "I LOVE YOU! ., ` ` ` ";

volatile char bit_array[MAX_CHARS*5];
int cursor=0;


/******************* String to Binary Conversion Macros ***************
 * Turn a numeric literal into a hex constant
 * (avoids problems with leading zeroes)
 * 8-bit constants max value 0x11111111, always fits in unsigned long
 **********************************************************************/

//------------------------------------------
// 	Helper macros (not for direct use)
//------------------------------------------

#define HEX__(n) 0x##n##LU

/* 8-bit conversion function */
#define B8__(x) ((x&0x0000000FLU)?1:0) \
+((x&0x000000F0LU)?2:0) \
+((x&0x00000F00LU)?4:0) \
+((x&0x0000F000LU)?8:0) \
+((x&0x000F0000LU)?16:0) \
+((x&0x00F00000LU)?32:0) \
+((x&0x0F000000LU)?64:0) \
+((x&0xF0000000LU)?128:0)


//------------------------------------------
// 			User macros
//------------------------------------------

/* for upto 8-bit binary constants */
#define B8(d) ((unsigned char)B8__(HEX__(d)))

/* for upto 16-bit binary constants, MSB first */
#define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<< + B8(dlsb))

/* for upto 32-bit binary constants, MSB first */
#define B32(dmsb,db2,db3,dlsb) (((unsigned long)B8(dmsb)<<24) + \
								((unsigned long)B8(db2)<<16) + \
								((unsigned long)B8(db3)<<8) + \
								B8(dlsb))

/**
 * Sample usage:
 * B8(01010101) = 85
 * B16(10101010,01010101) = 43605
 * B32(10000000,11111111,10101010,01010101) = 2164238933
 */


/******************************************************************************
 * LED matrix definitions
 ******************************************************************************/
// Columns are output on port 2
#define C1		(0x0002)
#define C2		(0x0004)
#define C3		(0x0008)
#define C4		(0x0010)
#define C5		(0x0020)

// Rows are output on port 1
#define R1		(0x0002)
#define R2		(0x0004)
#define R3		(0x0008)
#define R4		(0x0010)
#define R5		(0X0020)
#define R6		(0x0040)
#define R7		(0x0080)


/******************************************************************************
 * Dot matrix character pattern arrays
 * - The rightmost bit will always be ignored as the LED array is
 * only 5 pixels wide by 7 high
 ******************************************************************************/

volatile char bits_A[5] = {
		B8(11111000),
		B8(00100100),
		B8(00100010),
		B8(00100100),
		B8(11111000)
};

volatile char bits_B[5] = {
		B8(11111110),
		B8(10010010),
		B8(10010010),
		B8(10010010),
		B8(01101100)
};

volatile char bits_C[5] = {
		B8(01111100),
		B8(10000010),
		B8(10000010),
		B8(10000010),
		B8(01000100)
};

volatile char bits_D[5] = {
		B8(11111110),
		B8(10000010),
		B8(10000010),
		B8(01000100),
		B8(00111000)
};

volatile char bits_E[5] = {
		B8(11111110),
		B8(10010010),
		B8(10010010),
		B8(10010010),
		B8(10000010)
};

volatile char bits_F[5] = {
		B8(11111110),
		B8(00010010),
		B8(00010010),
		B8(00010010),
		B8(00000010)
};

volatile char bits_G[5] = {
		B8(01111100),
		B8(10000010),
		B8(10100010),
		B8(10100010),
		B8(01100100)
};

volatile char bits_H[5] = {
		B8(11111110),
		B8(00010000),
		B8(00010000),
		B8(00010000),
		B8(11111110)
};

volatile char bits_I[3] = {
		B8(10000010),
		B8(11111110),
		B8(10000010)
};

volatile char bits_J[5] = {
		B8(01100010),
		B8(10000010),
		B8(10000010),
		B8(01111110),
		B8(00000010)
};

volatile char bits_K[5] = {
		B8(11111110),
		B8(00010000),
		B8(00101000),
		B8(01000100),
		B8(10000010)
};

volatile char bits_L[5] = {
		B8(11111110),
		B8(10000000),
		B8(10000000),
		B8(10000000),
		B8(10000000)
};

volatile char bits_M[5] = {
		B8(11111110),
		B8(00000100),
		B8(00001000),
		B8(00000100),
		B8(11111110)
};

volatile char bits_N[5] = {
		B8(11111110),
		B8(00001000),
		B8(00010000),
		B8(00100000),
		B8(11111110)
};

volatile char bits_O[5] = {
		B8(01111100),
		B8(10000010),
		B8(10000010),
		B8(10000010),
		B8(01111100)
};

volatile char bits_P[5] = {
		B8(11111110),
		B8(00010010),
		B8(00010010),
		B8(00010010),
		B8(00001100)
};

volatile char bits_Q[5] = {
		B8(01111100),
		B8(10000010),
		B8(10100010),
		B8(01000010),
		B8(10111100)
};

volatile char bits_R[5] = {
		B8(11111110),
		B8(00010010),
		B8(00110010),
		B8(01010010),
		B8(10001100)
};

volatile char bits_S[5] = {
		B8(01001100),
		B8(10010010),
		B8(10010010),
		B8(10010010),
		B8(01100100)
};

volatile char bits_T[5] = {
		B8(00000010),
		B8(00000010),
		B8(11111110),
		B8(00000010),
		B8(00000010)
};

volatile char bits_U[5] = {
		B8(01111110),
		B8(10000000),
		B8(10000000),
		B8(10000000),
		B8(01111110)
};

volatile char bits_V[5] = {
		B8(00001110),
		B8(00110000),
		B8(11000000),
		B8(00110000),
		B8(00001110)
};

volatile char bits_W[5] = {
		B8(11111110),
		B8(01000000),
		B8(00100000),
		B8(01000000),
		B8(11111110)
};

volatile char bits_X[5] = {
		B8(11000110),
		B8(00101000),
		B8(00010000),
		B8(00101000),
		B8(11000110)
};

volatile char bits_Y[5] = {
		B8(00000110),
		B8(00001000),
		B8(11110000),
		B8(00001000),
		B8(00000110)
};

volatile char bits_Z[5] = {
		B8(11000010),
		B8(10100010),
		B8(10010010),
		B8(10001010),
		B8(10000110)
};

volatile char bits_SMILEY[5] = {
		B8(01100000),
		B8(10001110),
		B8(10000000),
		B8(10001110),
		B8(01100000)
};

volatile char bits_PERIOD[3] = {
		B8(00000000),
		B8(10000000),
		B8(00000000)
};

volatile char bits_COMMA[3] = {
		B8(10000000),
		B8(01000000),
		B8(00000000)
};

volatile char bits_EXCLAIM[3] = {
		B8(00000000),
		B8(10111110),
		B8(00000000)
};



/******************************************************************************
 * My functions
 ******************************************************************************/


/**
 * Sets a column of LEDs to the bit pattern in the parameter d
 * @param d the 8 bit pattern for the rows
 */
void setRows(volatile char d) {
	// The rightmost bit should remain unchanged by this function (P1.0, LED1 on launchpad)
	P1OUT |= 0xFE;				// set all the row outputs to high except rightmost bit
	P1OUT &= (0x01 | ~d);		// set flagged row outputs to low except rightmost bit
}

/**
 * Convert an array of characters to an array of bit patterns for display
 * @param 	str		pointer to the array of characters to display
 * @param 	disp 	pointer to the bit_array array
 */
void fillArray(const char *str, volatile char *disp) {
	size_t size = strlen(str);
	int i,j;
	// loop for each character in the string
	for (i=0; i<size; i++) {
		switch (str[i]) {
		case 'A':
			for (j=0; j<sizeof(bits_A); j++)
				disp[cursor++] = bits_A[j];
			break;
		case 'B':
			for (j=0; j<sizeof(bits_B); j++)
				disp[cursor++] = bits_B[j];
			break;
		case 'C':
			for (j=0; j<sizeof(bits_C); j++)
				disp[cursor++] = bits_C[j];
			break;
		case 'D':
			for (j=0; j<sizeof(bits_D); j++)
				disp[cursor++] = bits_D[j];
			break;
		case 'E':
			for (j=0; j<sizeof(bits_E); j++)
				disp[cursor++] = bits_E[j];
			break;
		case 'F':
			for (j=0; j<sizeof(bits_F); j++)
				disp[cursor++] = bits_F[j];
			break;
		case 'G':
			for (j=0; j<sizeof(bits_G); j++)
				disp[cursor++] = bits_G[j];
			break;
		case 'H':
			for (j=0; j<sizeof(bits_H); j++)
				disp[cursor++] = bits_H[j];
			break;
		case 'I':
			for (j=0; j<sizeof(bits_I); j++)
				disp[cursor++] = bits_I[j];
			break;
		case 'J':
			for (j=0; j<sizeof(bits_J); j++)
				disp[cursor++] = bits_J[j];
			break;
		case 'K':
			for (j=0; j<sizeof(bits_K); j++)
				disp[cursor++] = bits_K[j];
			break;
		case 'L':
			for (j=0; j<sizeof(bits_L); j++)
				disp[cursor++] = bits_L[j];
			break;
		case 'M':
			for (j=0; j<sizeof(bits_M); j++)
				disp[cursor++] = bits_M[j];
			break;
		case 'N':
			for (j=0; j<sizeof(bits_N); j++)
				disp[cursor++] = bits_N[j];
			break;
		case 'O':
			for (j=0; j<sizeof(bits_O); j++)
				disp[cursor++] = bits_O[j];
			break;
		case 'P':
			for (j=0; j<sizeof(bits_P); j++)
				disp[cursor++] = bits_P[j];
			break;
		case 'Q':
			for (j=0; j<sizeof(bits_Q); j++)
				disp[cursor++] = bits_Q[j];
			break;
		case 'R':
			for (j=0; j<sizeof(bits_R); j++)
				disp[cursor++] = bits_R[j];
			break;
		case 'S':
			for (j=0; j<sizeof(bits_S); j++)
				disp[cursor++] = bits_S[j];
			break;
		case 'T':
			for (j=0; j<sizeof(bits_T); j++)
				disp[cursor++] = bits_T[j];
			break;
		case 'U':
			for (j=0; j<sizeof(bits_U); j++)
				disp[cursor++] = bits_U[j];
			break;
		case 'V':
			for (j=0; j<sizeof(bits_V); j++)
				disp[cursor++] = bits_V[j];
			break;
		case 'W':
			for (j=0; j<sizeof(bits_W); j++)
				disp[cursor++] = bits_W[j];
			break;
		case 'X':
			for (j=0; j<sizeof(bits_X); j++)
				disp[cursor++] = bits_X[j];
			break;
		case 'Y':
			for (j=0; j<sizeof(bits_Y); j++)
				disp[cursor++] = bits_Y[j];
			break;
		case 'Z':
			for (j=0; j<sizeof(bits_Z); j++)
				disp[cursor++] = bits_Z[j];
			break;
		case '`':
			for (j=0; j<sizeof(bits_SMILEY); j++)
				disp[cursor++] = bits_SMILEY[j];
			break;
		case '.':
			for (j=0; j<sizeof(bits_PERIOD); j++)
				disp[cursor++] = bits_PERIOD[j];
			break;
		case ',':
			for (j=0; j<sizeof(bits_COMMA); j++)
				disp[cursor++] = bits_COMMA[j];
			break;
		case '!':
			for (j=0; j<sizeof(bits_EXCLAIM); j++)
				disp[cursor++] = bits_EXCLAIM[j];
			break;
		case ' ':
			for (j=0; j<3; j++)
				disp[cursor++] = 0x00;
		default:
			break;
		}

		// Add a space between characters
		disp[cursor++] = 0x00;
	}
}

/**
 * Program entry point
 */
int main(void) {

	WDTCTL = WDTPW + WDTHOLD;		// Stop watchdog timer
	P1DIR |= 0xFF;					// Set P1.x to output direction
	P2DIR |= 0xFF;					// Set P2.x to output direction

	// Turn off all outputs
	P1OUT = 0x00FE;
	P2OUT = 0x0000;

	// Fill up the bit pattern array
	fillArray(input, bit_array);

	volatile char current[5];		// array currently displayed
	int i, j, k, l, m;

	// start main loop
	// assume the whole display string has been stored in bit_array[] array
	while(1) {
		for (k=0; k<cursor; k++) {		// step through the display string 1 column at a time
			for (m=0; m<5; m++) {		// load up the current[] array with the 5 columns on display
				if (k+m < cursor)
					current[m] = bit_array[k+m];
				else
					current[m] = bit_array[k+m-cursor];
			}

			// test onboard Launchpad LED1 by toggling
			P1OUT ^= 0x01;

			for (l=SCROLL_SPEED; l>0; l--) {		// display the current[] array "speed" times
				for (i=0; i<5; i++) {		// step through each column of the led matrix
					switch (i) {
					case 0:
						P2OUT = 0x0000;			// turn off all columns
						setRows(current[0]);		// set the row outputs
						P2OUT = C1;				// turn on column 1
						break;
					case 1:
						P2OUT = 0x0000;			// turn off all columns
						setRows(current[1]);		// set the row outputs
						P2OUT = C2;
						break;
					case 2:
						P2OUT = 0x0000;			// turn off all columns
						setRows(current[2]);		// set the row outputs
						P2OUT = C3;
						break;
					case 3:
						P2OUT = 0x0000;			// turn off all columns
						setRows(current[3]);		// set the row outputs
						P2OUT = C4;
						break;
					case 4:
						P2OUT = 0x0000;			// turn off all columns
						setRows(current[4]);		// set the row outputs
						P2OUT = C5;
						break;
					default:
						// should never execute this default case
						P2OUT = 0x0000;
						setRows(0xFF);
						P2OUT = (C1|C2|C3|C4|C5);
						break;
					}

					for (j=LED_DELAY; j>0; j--);	// SW Delay
				}
			}
		}
	}
}
